package com.xiaoyung.easyisapisdk.feign.api.util;

import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson2.JSONObject;
import com.xiaoyung.easyisapisdk.common.auth.request.GetOpenApiTokenParam;
import com.xiaoyung.easyisapisdk.common.auth.response.TokenInfoResponse;
import com.xiaoyung.easyisapisdk.common.util.ExpiryMap;
import com.xiaoyung.easyisapisdk.common.util.ObjectDecoder;
import com.xiaoyung.easyisapisdk.common.util.ObjectEncoder;
import com.xiaoyung.easyisapisdk.feign.api.auth.OpenApiTokenApi;

import feign.Client;
import feign.Feign;
import feign.Feign.Builder;
import feign.Request;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Retryer;
import feign.form.FormEncoder;

public class EasyisOpenApiClient {
	private static Logger logger = LoggerFactory.getLogger(EasyisOpenApiClient.class);

	//public static final String TOKEN_PREFIX = "Bearer ";
    public static final String TOKEN_PREFIX = "";
	public static final String TOKEN_HEADER = "Authorization";
	
	private String url="https://easyis.xiaoyung.com/prod-api";
	/*private String appId ;*/
	private String accessKey;
	private String accessSecret;
	
	private String accessToken;//可以直接传入这个 不用通过ak,sk获取
	
	private Boolean camleNameModel = true;// 驼峰模式 默认true 所有字段key转成驼峰模式 
	//private JSONObject headers;
	
	private Map<Class<?>, Object> apiCach = new ConcurrentHashMap<>();
	private Builder apiBuilder = null;
	
	public Client feignClient() {
		try {
			SSLContext ctx = SSLContext.getInstance("SSL");
			X509TrustManager tm = new X509TrustManager() {
				@Override
				public void checkClientTrusted(X509Certificate[] chain, String authType) {
				}
				@Override
				public void checkServerTrusted(X509Certificate[] chain, String authType) {
				}
				@Override
				public X509Certificate[] getAcceptedIssuers() {
					//如果这里后续报空指针，就return new X509Certificate[0]
					return null;
				}
			};
			ctx.init(null, new TrustManager[] { tm }, null);
			return new Client.Default(ctx.getSocketFactory(), (hostname, session) -> true);
		
		}catch(Exception ex) {
			throw new RuntimeException(ex);
		}
	}
	public EasyisOpenApiClient(JSONObject conf) {// String appId, String accessKey, String accessSecret
		
		String url = conf.getString("url");
		String accessKey = conf.getString("accessKey");
		String accessSecret = conf.getString("accessSecret");
		String accessToken = conf.getString("accessToken");
		Boolean camleNameModel = conf.getBoolean("camleNameModel");
		JSONObject headers = conf.getJSONObject("headers");
		
		if(!StringUtils.isEmpty(url)) {
			this.url = url;
		}
		if(headers!=null && !headers.isEmpty()) {
			if(this.header==null) {
				this.header = new HashMap<>();
			}
			for(Map.Entry<String, Object> entry : headers.entrySet()) {
				this.header.put(entry.getKey(), entry.getValue().toString());
			}
		}
		
		this.accessKey = accessKey;
		this.accessSecret = accessSecret;
		this.accessToken = accessToken;
		if (camleNameModel == null) {
			// java默认开启 返回值是驼峰模式 传入参数(表单字段insert之类的 驼峰,下划线都支持)
			this.camleNameModel = true;
		}else {
			this.camleNameModel = camleNameModel;
		}
		
		EasyisOpenApiClient that = this;
		apiBuilder = Feign.builder()
			.client(feignClient())//https忽略证书
			.decoder(new ObjectDecoder(that.camleNameModel))
			//.encoder(new ObjectEncoder())
			//.encoder(new FormEncoder(new JacksonEncoder()))
			.encoder(new FormEncoder(new ObjectEncoder()))
			.options(new Request.Options(50000,TimeUnit.MILLISECONDS, 50000,TimeUnit.MILLISECONDS,true))
			.retryer(Retryer.NEVER_RETRY)// Retryer.Default(5000, 5000, 3)
			.requestInterceptor(new RequestInterceptor() {
				@Override
				public void apply(RequestTemplate template) {
					if(header!=null && !header.isEmpty()) {
						for(Map.Entry<String, String> entry : header.entrySet()) {
							template.header(entry.getKey(),entry.getValue());
						}
					}
					if(that.camleNameModel) {
						template.query("camleNameModel", that.camleNameModel+"");
					}
//					template.header("x-lb-first-ip","");

					logger.debug("调用 openapi : {} {} ",that.getUrl(),template.path());
					if(!template.path().contains("/openapi/getToken")) {//  template.path= /openapi/form/dataService/selectById
						template.header(TOKEN_HEADER,TOKEN_PREFIX +getOpenApiToken());//XmisSmsUtil.getToken()
					}
				}
			});
	}
	
	public Map<String,String> header = null;
	public void addHeader(String key,String value) {
		if(header == null) {
			header = new HashMap<>();
		}
		header.put(key, value);
	}
	public void setHeader(Map<String,String> headerMap) {
		this.header=headerMap;
	}
	public Map<String, String> getHeader() {
		return header;
	}
	
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	
	public Boolean getCamleNameModel() {
		return camleNameModel;
	}

	public void setCamleNameModel(Boolean camleNameModel) {
		this.camleNameModel = camleNameModel;
	}

	@SuppressWarnings("unchecked")
	public <T> T getApi(Class<?> service) {
		T api = (T)apiCach.get(service);
		if(api==null) {
			synchronized (EasyisOpenApiClient.class) {
				if(api==null) {
					api =  (T) apiBuilder.target(service, this.url);
					apiCach.put(service, api);
				}
			}
		}
		return api;
	}
	
	Map<String, TokenInfoResponse> openApiTokenCach = null;
	private static String TOKEN_INFO = "tokenInfo";
	
	//private static String EASYIS_OPENAPI_ACCESS_TOKEN = "easyis.openapi.accessToken";
	public synchronized String getOpenApiToken() {
		//云函数传递过来的accessToken
		//String accessToken = System.getProperty(EASYIS_OPENAPI_ACCESS_TOKEN);
		if(!StringUtils.isEmpty(accessToken)) {
			return accessToken;
		}
		
		TokenInfoResponse token = openApiTokenCach == null ? null : openApiTokenCach.get(TOKEN_INFO);
		//过期 重新获取
		if (token == null) {
			GetOpenApiTokenParam param = new GetOpenApiTokenParam();
			param.setAccessKey(this.accessKey);
			param.setAccessSecret(this.accessSecret);
			
			OpenApiTokenApi openApiTokenApi = this.getApi(OpenApiTokenApi.class);
			
			token = openApiTokenApi.getToken(param);
			//logger.debug("调用 getToken : {} ",token);
			openApiTokenCach = new ExpiryMap<>((token.getExpiresIn() - 60) * 1000);// 提前60s 过期
			openApiTokenCach.put(TOKEN_INFO, token);
			
		}
		return token.getAccessToken();
	}

	
}
